using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for Net.
	/// </summary>
	public class Net
	{
		System.Collections.ArrayList oSourceNodes;
		System.Collections.ArrayList oSinkNodes;
		System.Collections.ArrayList oPassThroughNodes;

        System.Collections.ArrayList oTraceList;
        bool bTrace;

		SimulationState eSimulationStateOutput;
		NodeState eNetState;

		double dPropagationDelay;

		EventNode oEventNodeStabilize;
		RapidHardware oRapidHardware;

		// destabilize
		// stabilize

        /// <summary>
        /// Constructor to create a new net
        /// </summary>
        /// <param name="poNode">A net must have at least one node</param>
		public Net(Node poNode)
		{
			oEventNodeStabilize = null;
			dPropagationDelay = 0;
			eNetState = NodeState.Undefined;
			oSourceNodes = new System.Collections.ArrayList();
			oSinkNodes = new System.Collections.ArrayList();
			oPassThroughNodes = new System.Collections.ArrayList();
			eSimulationStateOutput = SimulationState.Stable;
			oRapidHardware = poNode.ParentNodeVector.ParentComponent.RapidHardware;
			this.Connect(poNode);
		}

        /// <summary>
        /// For debug purposes.
        /// Print the members of this net.
        /// Can be called from a debug session if there is a problem with the structure of RTL. 
        /// </summary>
        public void DebugDisplayNet()
        {
            DebugDisplayNet(this);
        }

        /// <summary>
        /// For debug purposes.
        /// Print the members of a net.
        /// Can be called from a debug session if there is a problem with the structure of RTL. 
        /// </summary>
        /// <param name="poNet">The net to display</param>
        public void DebugDisplayNet(Net poNet)
        {
            Console.WriteLine("---------- NET -------------- " + poNet.NetName);
            Console.WriteLine("Source Nodes");
            foreach (Node oNode in poNet.SourceNodes)
            {
                Console.WriteLine(oNode.ParentNodeVector.HirarchicalName + " " + oNode.Position.ToString());
            }
            Console.WriteLine("Sink Nodes");
            foreach (Node oNode in poNet.SinkNodes)
            {
                Console.WriteLine(oNode.ParentNodeVector.HirarchicalName + " " + oNode.Position.ToString());
            }
            Console.WriteLine("Pass Nodes");
            foreach (Node oNode in poNet.PassthroughNodes)
            {
                Console.WriteLine(oNode.ParentNodeVector.HirarchicalName + " " + oNode.Position.ToString());
            }
            Console.WriteLine("---------- END --------------");
        }


        /// <summary>
        /// Connect all the nodes in a different net to this net
        /// </summary>
        /// <param name="poNet">node to be added to this net</param>
		public void Connect(Net poNet)
		{
			if (this.NodeCount < poNet.NodeCount)
			{
				poNet.Connect(this);
				return;
			}
            
            //DebugDisplayNet();
            //DebugDisplayNet(poNet);

			foreach(Node oNode in poNet.SourceNodes)
			{
				this.Connect(oNode);
			}
			foreach(Node oNode in poNet.SinkNodes)
			{
				this.Connect(oNode);
			}
			foreach(Node oNode in poNet.PassthroughNodes)
			{
				this.Connect(oNode);
			}
		}

        /// <summary>
        /// Connect another node to this net
        /// </summary>
        /// <param name="poNode">node to be added to this net</param>
		public void Connect(Node poNode)
		{
			switch (poNode.NodeFlowType)
			{
				case NodeFlowType.Source:
					oSourceNodes.Add(poNode);
					break;
				case NodeFlowType.Sink:
					oSinkNodes.Add(poNode);
					break;
				case NodeFlowType.Passthrough:
					oPassThroughNodes.Add(poNode);
					break;
			}				
			poNode.Net = this;
		}

        /// <summary>
        /// get the number of nodes in this net
        /// </summary>
		public int NodeCount
		{
			get
			{
				return oPassThroughNodes.Count + oSinkNodes.Count + oSourceNodes.Count;
			}
		}

        /// <summary>
        /// Used in simulation mode only.
        /// Determine if any inputs to this net are unstable or not.
        /// </summary>
		public SimulationState SimulationStateInput
		{
			get
			{
				foreach(Node oNode in oSourceNodes)
				{
					if (oNode.ParentNodeVector.ParentComponent.SimulationStateOutput == SimulationState.Unstable)
					{
						//SimulationTraceLog("This net's input node is unstable.",oNode);
						return SimulationState.Unstable;
					}
				}
				//SimulationTraceLog("This net's inputs are stable.",null);
				return SimulationState.Stable;
			}
		}

        /// <summary>
        /// Used in simulation only, get or set the state of the net.
        /// Only nets without source nodes may be set, otherwise the source nodes termine the net's state.
        /// </summary>
		public NodeState NodeState
		{
			get
			{
				if (oSourceNodes.Count == 0)
					return eNetState;

				NodeState eNodeState = NodeState.Tri;
				foreach(Node oNode in oSourceNodes)
				{
					if (oNode.NodeState != NodeState.Tri)
					{
						if (eNodeState == NodeState.Tri)
							eNodeState = oNode.NodeState;
						else
						{
							if (eNodeState != oNode.NodeState)
								return NodeState.Undefined;
						}
					}
				}
				return eNodeState;
			}

			set
			{
                if (oSourceNodes.Count == 0)
                {
                    if (eNetState != value)
                    {
                        eNetState = value;
                    }
                }
			}
		}

        /// <summary>
        /// Used in Verilog Creation
        /// </summary>
        /// <param name="poComponent"></param>
        /// <returns></returns>
		public Node SelectCriticalNode(Component poComponent)
		{
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NetStructure.Connection | oNode.NodeStructure == NetStructure.Input | oNode.NodeStructure == NetStructure.Output)
						return oNode;
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NetStructure.Connection | oNode.NodeStructure == NetStructure.Input | oNode.NodeStructure == NetStructure.Output)
						return oNode;
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					if(oNode.NodeStructure == NetStructure.Connection | oNode.NodeStructure == NetStructure.Input | oNode.NodeStructure == NetStructure.Output)
						return oNode;
			}

			return null;
		}

        /// <summary>
        /// used for verilog generation, return nodes from thsi net matching a criteria
        /// </summary>
        /// <param name="poComponent">select only nodes from this component</param>
        /// <param name="peNetStructure">select only nodes of this structure type</param>
        /// <returns>list of nodes from this net</returns>
		public System.Collections.ArrayList SelectNodes(Component poComponent, NetStructure peNetStructure)
		{
			System.Collections.ArrayList oResult = new System.Collections.ArrayList();
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNetStructure)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNetStructure)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent & oNode.NodeStructure == peNetStructure)
					oResult.Add(oNode);
			}

			return oResult;
		}


        /// <summary>
        /// used for verilog generation, return nodes from this net matching a criteria
        /// </summary>
        /// <param name="poComponent">select only nodes from this component</param>
        /// <returns>list of nodes from this net</returns>
        public System.Collections.ArrayList SelectNodes(Component poComponent)
		{
			System.Collections.ArrayList oResult = new System.Collections.ArrayList();
			foreach(Node oNode in oSourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oSinkNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			foreach(Node oNode in oPassThroughNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent == poComponent)
					oResult.Add(oNode);
			}

			return oResult;
		}

        /// <summary>
        /// return the output state of this net, used for simulation
        /// </summary>
		public SimulationState SimulationStateOutput
		{
			get{return eSimulationStateOutput;}
		}

        /// <summary>
        /// ArrayList of nodes that are neither sources nor sinks, but passthrough conduits only
        /// </summary>
		public System.Collections.ArrayList PassthroughNodes
		{
			get {return oPassThroughNodes;}
		}

        /// <summary>
        /// Arraylist of nodes that are sources in this net.  Note, if using tristates, you may have more than one source.
        /// </summary>
		public System.Collections.ArrayList SourceNodes
		{
			get {return oSourceNodes;}
		}

        /// <summary>
        /// Arraylist of nodes that are sinks, or consumers, of this net.
        /// </summary>
		public System.Collections.ArrayList SinkNodes
		{
			get {return oSinkNodes;}
		}

        /// <summary>
        /// used by simulation to mark this network as unstable
        /// </summary>
		public void SimulationDestabilizeNet()
		{
			// if net output is already unstable...
			//		we only need to cancel any scheduled stabilization event
			if (eSimulationStateOutput == SimulationState.Unstable)
			{
				SimulationTraceLog("net is already unstable",null); 

				// cancel stabilization event
				if (oEventNodeStabilize != null)
					oRapidHardware.Simulation.Clock.UnlinkNode(oEventNodeStabilize);
				oEventNodeStabilize = null;
				return;
			}

			// otherwise, we were a net stable, 
			// we need to alert sink nodes in the net that we are now unstable
			eSimulationStateOutput = SimulationState.Unstable;
			int i = 1;
			foreach(Node oNode in oSinkNodes)
			{
                Component oComponent = oNode.ParentNodeVector.ParentComponent;
                if (!oComponent.RegisteredComponent)
                {
                    SimulationTraceLog("Destabilizing Component {" + oNode.ParentNodeVector.ParentComponent.HirarchicalName + "} from Net via Node, loop " + i.ToString() + " of " + oSinkNodes.Count.ToString(), oNode);
                    oComponent.SimulationDestabilizeComponent();
                }
			}
		}

        /// <summary>
        /// used by simulation to mark this net as stable
        /// </summary>
		public void SimulationStabilizeNet()
		{
			// should never call this when node is already stable
			System.Diagnostics.Debug.Assert(eSimulationStateOutput != SimulationState.Stable);

            // debug trace stabililzation event
            if (bTrace)
            {
                if (oTraceList.Contains("stabilize"))
                {
                    System.Console.Write("Break for {stabilize} " + this.NodeState + " - " + this.NetName + "\n");
                    oRapidHardware.SystemInterface.ListAll();
                    System.Diagnostics.Debugger.Break();
                }
            }

			// check to see if the inputs are all stable
			if (this.SimulationStateInput != SimulationState.Stable)
			{
				return;  // unstable inputs mean unstable net
			}

			// if there is no propagation delay, immediately stabilize output too
			//     but, if there is a propagation delay, schedule stabilization event on clock
            // hack: hardcoded propagation delay, try to fix hold time failures on registers
			if (dPropagationDelay == 0)
			{
				SimulationStabilizeNetOutputEvent(0);
                //dPropagationDelay = (1.0 / (double)oRapidHardware.Simulation.Clock.Frequency * .01);
			}
			else
			{
				if (oEventNodeStabilize != null)
					oRapidHardware.Simulation.Clock.UnlinkNode(oEventNodeStabilize);
				oEventNodeStabilize = oRapidHardware.Simulation.Clock.ScheduleClockEvent(dPropagationDelay,this.SimulationStabilizeNetOutputEvent); 
			 }
		}

        /// <summary>
        /// simulation event delegate, stabilize the net at specified time 
        /// </summary>
        /// <param name="pdTimeNow">time (after delay) when stabilization event is scheduled in event clock</param>
		public void SimulationStabilizeNetOutputEvent(double pdTimeNow)
		{

			SimulationTraceLog("SimulationSabilizeComponentOutputEvent at time " + pdTimeNow.ToString() + "/" + oRapidHardware.Simulation.Clock.TimeNow.ToString() ,null);

			// if oEventNode is not null when calling directly, then there is a fundamental scheduling problem
			System.Diagnostics.Debug.Assert(oEventNodeStabilize == null & pdTimeNow == 0 | pdTimeNow != 0,"Phantom EventNodeStabilize event scheduled in Net object!");

			// should never call this event if node is already stable
			System.Diagnostics.Debug.Assert(eSimulationStateOutput != SimulationState.Stable,"Net stabilization event called when net is already stable!");

			eSimulationStateOutput = SimulationState.Stable;

			int i = 1;
			foreach(Node oNode in oSinkNodes)
			{
                Component oComponent = oNode.ParentNodeVector.ParentComponent;
                if (!oComponent.RegisteredComponent)
                {
                    SimulationTraceLog("Stabilizing Component {" + oNode.ParentNodeVector.ParentComponent.HirarchicalName + "} from Net via Node, loop " + i.ToString() + " of " + oSinkNodes.Count.ToString(), oNode);
                    oComponent.SimulationStabilizeComponent();
                }
				i++;
			}	
		}

        /// <summary>
        /// Log the simulation state variables of a particular node
        /// </summary>
        /// <param name="psLogEntry">information is added to this input string</param>
        /// <param name="poNode">state data of this node is logged</param>
		private void SimulationTraceLog(string psLogEntry, Node poNode)
		{
			string sLogEntry = "Net: ";
			sLogEntry += psLogEntry;
			if (poNode != null)
				sLogEntry += " {Node: " + poNode.ParentNodeVector.ParentComponent.HirarchicalName.ToString() + "." + poNode.Name + " = '" + poNode.NodeState.ToString() + "'}";
			oRapidHardware.InternalLog.Log(LogType.SimulationTrace,sLogEntry);
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="psLabel"></param>
        public void SetTrace(string psLabel)
        {
            if (!bTrace)
            {
                bTrace = true;
                oTraceList = new System.Collections.ArrayList();
            }
            if (oTraceList.Contains(psLabel))
                return;
            oTraceList.Add(psLabel);
        }

        /// <summary>
        /// returns the connections of the structure of this network,
        /// using source hirarchical names 
        /// </summary>
        public string NetName
        {
            get
            {
                string sName = "";
                Node oNode;
                if (oSinkNodes.Count > 0)
                {
                    oNode = (Node)oSinkNodes[0];
                    sName += oNode.HirarchicalName;
                }
                sName += "<=";
                if (oSourceNodes.Count > 0)
                {
                    oNode = (Node)oSourceNodes[0];
                    sName += oNode.HirarchicalName;
                }

                return sName;
            }
        }

        /// <summary>
        /// used only for simulation,
        /// force the state of all nodes in this net
        /// </summary>
        /// <param name="pNodeState">state to force</param>
        public void ForceNodeState(NodeState pNodeState)
        {
            if (this.NodeState == pNodeState)
                return;
            this.NodeState = pNodeState;
            if (this.NodeState == pNodeState)
                return;
            foreach (Node oNode in SourceNodes)
            {
                oNode.NodeState = pNodeState;
            }
        }

        /// <summary>
        /// check to see if a node belongs to this net
        /// </summary>
        /// <param name="poNode">node we are seeking</param>
        /// <returns>true if the node belongs to this net</returns>
        public bool ContainsNode(Node poNode)
        {
            foreach (Node oNode in oSourceNodes)
            {
                if (oNode == poNode)
                    return true;
            }
            foreach (Node oNode in oSinkNodes)
            {
                if (oNode == poNode)
                    return true;
            }
            foreach (Node oNode in oPassThroughNodes)
            {
                if (oNode == poNode)
                    return true;
            }
            return false;
        }

	}
}
